package com.demon.framework.boot.autoconfigure.scheduler;

import com.demon.framework.boot.context.SpringContext;
import com.demon.framework.boot.failover.TaskFailoverService;
import com.demon.framework.boot.job.JobForTaskService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerListener;
import org.quartz.impl.StdScheduler;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.util.ClassUtils;

import java.util.List;
import java.util.Properties;

/**
 * 定时器配置
 *
 * @author Demon-HY
 * @date 2020/7/24 16:42
 */
@Slf4j
@Configuration
public class QuartzAutoConfiguration {

    @Value("${quartz.datasource.url:${spring.datasource.url}}")
    private String jdbcUrl;
    @Value("${quartz.datasource.username:${spring.datasource.username}}")
    private String jdbcUser;
    @Value("${quartz.datasource.password:${spring.datasource.password}}")
    private String jdbcPwd;
    @Value("${quartz.datasource.driver-class-name:${spring.datasource.driver-class-name}}")
    private String jdbcDrive;
    @Value("${quartz.thread.count:15}")
    private String quartzThreadCount;
    @Value("${quartz.jdbc.conn.max:15}")
    private String jdbcMaxConnections;
    @Value("${quartz.scheduler.instanceName}")
    private String instanceName;

    private final static String QUARTX_DS = "quartxDS";

    /**
     * 设置quartz属性
     */
    private Properties quartzProperties() {
        Properties prop = new Properties();

        prop.put("org.quartz.scheduler.instanceName", instanceName);

        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        prop.put("org.quartz.scheduler.instanceIdGenerator.class", ClassUtils.getQualifiedName(QuartzInstanceIdGenerator.class));

        prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
        prop.put("org.quartz.scheduler.jobFactory.class", "org.quartz.simpl.SimpleJobFactory");
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
        prop.put("org.quartz.jobStore.dataSource", "quartxDS");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", quartzThreadCount);

        prop.put("org.quartz.dataSource." + QUARTX_DS + ".provider", "hikaricp");
        prop.put("org.quartz.dataSource." + QUARTX_DS + ".driver", jdbcDrive);
        prop.put("org.quartz.dataSource." + QUARTX_DS + ".URL", jdbcUrl);
        prop.put("org.quartz.dataSource." + QUARTX_DS + ".user", jdbcUser);
        prop.put("org.quartz.dataSource." + QUARTX_DS + ".password", jdbcPwd);
        prop.put("org.quartz.dataSource." + QUARTX_DS + ".maxConnections", jdbcMaxConnections);
        return prop;
    }


    /**
     * 主要目的就是解决job类 注入其他service或者使用Spring组件
     */
    @Bean
    public AdaptableJobFactory jobFactory(AutowireCapableBeanFactory capableBeanFactory) {
        return new AdaptableJobFactory() {
            @Override
            protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
                try {
                    return SpringContext.getBean(ClassUtils.getShortNameAsProperty(bundle.getJobDetail().getJobClass()));
                } catch (Exception e) {
                    log.error("spring 上下文中获取 {} 失败", bundle.getJobDetail().getJobClass(), e);
                    //调用父类的方法
                    Object jobInstance = super.createJobInstance(bundle);
                    //进行注入
                    capableBeanFactory.autowireBean(jobInstance);
                    return jobInstance;
                }
            }
        };
    }

//    public static void main(String[] args) {
//        System.out.println(AdaptableJobFactory.class.getSimpleName());
//
//        System.out.println(ClassUtils.classNamesToString(AdaptableJobFactory.class));
//
//        System.out.println(ClassUtils.getQualifiedName(AdaptableJobFactory.class));
//        System.out.println(ClassUtils.getShortNameAsProperty(AdaptableJobFactory.class));
//    }

    /**
     * 任务调度 日志监听
     */
    @Bean
    public LogSchedulerListener logSchedulerListener() {
        return new LogSchedulerListener();
    }

    /**
     * 注册任务调度中心
     */
    @Bean
    @ConditionalOnMissingBean
    public Scheduler scheduler(AdaptableJobFactory jobFactory, @Autowired(required = false) List<SchedulerListener> sls) throws SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory(quartzProperties());
        StdScheduler scheduler = (StdScheduler) schedulerFactory.getScheduler();
        scheduler.setJobFactory(jobFactory);
        scheduler.start();

        if (null != sls && !sls.isEmpty()) {
            sls.forEach(schedulerListener -> {
                String listenerClassName = schedulerListener.getClass().getCanonicalName();
                try {
                    scheduler.getListenerManager().addSchedulerListener(schedulerListener);
                    log.info("qrtz任务监听器注入 {} 成功", listenerClassName);
                } catch (SchedulerException e) {
                    log.warn("qrtz任务监听器注入 {} 失败！", listenerClassName);
                }
            });
        }

        return scheduler;
    }

    /**
     * 任务调度 工具
     */
    @Bean
    public JobForTaskService jobForTaskService(Scheduler scheduler) {
        return new JobForTaskService(scheduler);
    }

    /**
     * 业务异常重试 工具
     */
    @Bean
    @ConditionalOnMissingBean
    public TaskFailoverService taskFailoverService(JobForTaskService jobForTaskService,
                                                   AutowireCapableBeanFactory capableBeanFactory,
                                                   JdbcTemplate jdbcTemplate) {
        return new TaskFailoverService(jobForTaskService, capableBeanFactory, jdbcTemplate);
    }
}
